/*
 * \brief  Generic root component implementation
 * \author Norman Feske
 * \date   2006-05-22
 *
 * This class is there for your convenience.
 * If performs the common actions that must
 * always be taken when creating a new session.
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _INCLUDE__ROOT__COMPONENT_H_
#define _INCLUDE__ROOT__COMPONENT_H_

#include <root/server.h>
#include <base/heap.h>
#include <ram_session/ram_session.h>
#include <util/arg_string.h>

namespace Genode {

	/*
	 * \param SESSION_TYPE  session-component type to manage,
	 *                      derived from 'Server_object'
	 */
	template <typename SESSION_TYPE>
	class Root_component : public Root_server
	{
		private:

			/*
			 * Entry point that manages the session objects
			 * created by this root interface
			 */
			Server_entrypoint *_ep;

			/*
			 * Allocator for allocating session objects.
			 * This allocator must be used by the derived
			 * class when calling the 'new' operator for
			 * creating a new session.
			 */
			Allocator *_md_alloc;

		protected:

			/**
			 * Create new session (to be implemented by a derived class)
			 *
			 * Only a derived class knows the constructor arguments of
			 * a specific session. Therefore, we cannot unify the call
			 * of its 'new' operator and must implement the session
			 * creation at a place, where the required knowledge exist.
			 *
			 * In the implementation of this function, the heap, provided
			 * by 'Root_component' must be used for allocating the session
			 * object.
			 *
			 * \throw Allocator::Out_of_memory  typically caused by the
			 *                                  meta-data allocator
			 * \throw Root::Invalid_args        typically caused by the
			 *                                  session-component constructor
			 */
			virtual SESSION_TYPE *_create_session(const char *args) = 0;

			virtual void _destroy_session(SESSION_TYPE *session) {
				destroy(_md_alloc, session); }

			/**
			 * Return allocator to allocate server object in '_create_session()'
			 */
			Allocator         *md_alloc() { return _md_alloc; }
			Server_entrypoint *ep()       { return _ep; }

		public:

			/**
			 * Constructor
			 *
			 * \param ep           entry point that manages the sessions of this
			 *                     root interface.
			 * \param ram_session  provider of dataspaces for the backing store
			 *                     of session objects and session data
			 */
			Root_component(Server_entrypoint *ep, Allocator *metadata_alloc)
			: Root_server(), _ep(ep), _md_alloc(metadata_alloc) { }


			/********************
			 ** Root interface **
			 ********************/

			Session_capability session(const char *args)
			{
				/*
				 * We need to decrease 'ram_quota' by
				 * the size of the session object.
				 */
				size_t ram_quota = Arg_string::find_arg(args, "ram_quota").long_value(0);
				long remaining_ram_quota = ram_quota - sizeof(SESSION_TYPE);
				if (remaining_ram_quota < 0) {
					PERR("Insufficient ram quota, provided=%zd, required=%zd",
					     ram_quota, sizeof(SESSION_TYPE));
					throw Quota_exceeded();
				}

				SESSION_TYPE *s = 0;
				try {
					s = _create_session(args);
				} catch (Allocator::Out_of_memory) {
					throw Quota_exceeded();
				}

				return Session_capability(_ep->manage(s));
			}

			void close(Session_capability session)
			{
				SESSION_TYPE *s;

				s = dynamic_cast<SESSION_TYPE *>(_ep->obj_by_cap(session));
				if (!s) return;

				/* let the entry point forget the session object */
				_ep->dissolve(s);

				_destroy_session(s);
				return;
			}
	};
}

#endif /* _INCLUDE__ROOT__COMPONENT_H_ */
